#include "ftpget.h"

int debug = 0;

/* Funcao para iniciar um socket
 * @param host	endereço do servidor
 * @param port	porta do servidor
 * @return status da conexao ( -1 em caso de erro)
 */
int createSocket(char *host, int port) {
	int sockfd;
	struct sockaddr_in server_addr;

	struct hostent *h;

	if ((h = gethostbyname(host)) == NULL) {
		herror("gethostbyname");
		exit(1);
	}

	if(debug) {
		printf("Host name  : %s\n", h->h_name);
		printf("IP Address : %s\n", inet_ntoa(*((struct in_addr *) h->h_addr)));
	}

	/*server address handling*/
	bzero((char*) &server_addr, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr(
			inet_ntoa(*((struct in_addr *) h->h_addr))); /*32 bit Internet address network byte ordered*/
	server_addr.sin_port = htons(port); /*server TCP port must be network byte ordered */

	/*open an TCP socket*/
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket()");
		exit(0);
	}

	/*connect to the server*/
	if (connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr))
			< 0) {
		perror("connect()");
		exit(-1);
	}

	return sockfd;
}

/* Funcao para receber dados pela porta 21 (Porta de controlo FTP)
 * @param sockfd	socket para receção de dados
 * @return	0 se tudo ocorreu bem
 *		   	> 0 com o numero da porta para o modo passivo
 *		   	< 0 para erros (-1 para login incorreto)
 */
int receive(int sockfd) {
	char response[MAX_LENGTH];
	int bytes;	// bytes lidos

	bytes = read(sockfd, response, MAX_LENGTH);
	if(bytes < 0)  // Erro na receção de dados
		return -1;
	response[bytes - 2] = '\0';
	if (debug)
		printf("Recebeu (%d): [%s]\n", bytes, response);

	//caso seja a resposta com os dados da porta para o modo passivo
	if (!strncmp(response, "227", 3))
		return calcPort(response);	//retorna a porta para o modo passivo

	if (!strncmp(response, "530", 3))
			return -1;	//retorna a porta para o modo passivo

	return 0;
}

/* Funcao para receber e guardar ficheiro via socket
 * @param sockfd	socket para receção
 * @param fname		nome do ficheiro a ser gravado em disco
 * @return  > 0 se tudo ocorreu bem
 * 			> 0 se houve erros
 */
int receiveFile(int sockfd, char *fname) {
	int fd, bytes;
	char buffer[MAX_LENGTH];

	printf("Gravando arquivo: %s\n", fname);
	fd = fopen(fname, "w");

	while ((bytes = read(sockfd, buffer, MAX_LENGTH)) > 0)
		if (bytes > 0)
			fwrite(buffer, bytes, 1, fd);

	fclose(fd); //fecha descritor do ficheiro
	printf("Ficheiro salvo.\n");

	return bytes;
}

/* Funcao para calcular o numero da porta de receção de dados
 * @param	str		string que contem a resposta 227 do servidor FTP
 * @return 	numero da porta para receção de dados
 * 			< 0 em caso de erros
 */
int calcPort(char *str) {
	int byte0, byte1;
	int i, j, commas;
	char port[4];

	//Conta 4 virgulas na string
	for (i = 0, commas = 0; commas < 4; i++) {
		if (str[i] == ',')
			commas++;
	}
	//Aqui a posicao str[i] guarda o primeiro digito do byte1

	//Pega os digitos ate encontrar uma virgula
	for (j = 0; str[i] != ','; i++, j++) {
		port[j] = str[i];
	}
	port[j] = '\0';
	byte1 = atoi(port);

	i++;
	// Aqui a posicao str[i] guarda o primeiro digito do byte0

	for (j = 0; str[i] != ')'; i++, j++) {
		port[j] = str[i];
	}
	port[j] = '\0';
	byte0 = atoi(port);

	return (256 * byte1 + byte0);
}

/* Funcao para pegar o nome do ficheiro dentro de uma string url
 * @param	url		string contendo a url com o nome do ficheiro
 * @return	o nome do ficheiro dentro da url
 */
char *getName(char *url) {
	char *fname;

	fname = url;
	url = strtok(url, "/");

	while (url != NULL) {
		//if(debug)printf("%s\n", url); // DEBUG
		fname = url;
		url = strtok(NULL, "/");
	}

	return fname;
}

/* Funcao para fazer o download de um ficheiro via FTP
 * @param	host	endereço do servidor FTP
 * @param	url		endereço do ficheiro no servidor FTP
 * @param	login	usuario para autenticação no servidor
 * @param	pass	password para autenticação no servidor
 */
void ftpget(char *host, char *url, char *login, char *pass) {
	int ftp_sockfd, down_sockfd, tPort, bytes;
	char buffer[MAX_PATH]; // Nome do ficheiro para gravação

	//Inicializacao do socket de controlo FTP
	ftp_sockfd = createSocket(host, FTP_PORT);
	receive(ftp_sockfd);

	// Envio de Username
	sprintf(buffer, "user %s\n", login);
	bytes = write(ftp_sockfd, buffer, strlen(buffer));
	if (debug)
		printf("Escreveu (%d): %s", bytes, buffer);
	receive(ftp_sockfd);

	// Envio de Password
	sprintf(buffer, "pass %s\n", pass);
	bytes = write(ftp_sockfd, buffer, strlen(buffer));
	if (debug)
		printf("Escreveu (%d): %s", bytes, buffer);
	if ( receive(ftp_sockfd) == -1 ) {
		printf("Login ou Senha incorretos.\n");
		exit(-0);
	}

	// Entrando no modo passivo
	sprintf(buffer, "pasv\n");
	bytes = write(ftp_sockfd, buffer, strlen(buffer));
	if (debug)
		printf("Escreveu (%d): %s", bytes, buffer);
	tPort = receive(ftp_sockfd);

	// Inicializando a porta de receção de dados do modo passivo
	if (tPort) {
		down_sockfd = createSocket(host, tPort);
	} else {
		printf("Erro ao abrir a porta de rececao.\n");
		exit(-1);
	}

	// Aquisição do ficheiro
	sprintf(buffer, "retr %s\n", url);
	bytes = write(ftp_sockfd, buffer, strlen(buffer));
	if (debug)
		printf("Escreveu (%d): %s", bytes, buffer);
	receive(ftp_sockfd);
	receiveFile(down_sockfd, getName(url));

	//Fechamento dos sockets
	close(ftp_sockfd);
	close(down_sockfd);

}

